package io.renren.modules.medical.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.renren.common.exception.ScriptCallException;
import io.renren.common.exception.ThreadPoolException;
import io.renren.common.result.Result;
import io.renren.common.result.ResultCodeEnum;
import io.renren.common.utils.RemoteThread;
import io.renren.modules.medical.service.RemoteService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.*;
import java.net.Socket;
import java.util.concurrent.*;

/**
 * @author Luo Sheng
 * @school YiBin university
 * @create 2023-02-21 19:49
 */
@Slf4j
@Service
public class RemoteServiceImpl implements RemoteService {
    private final String HOST; // python主机名

    private final Integer PORT; // python端口

    // 线程池对象
    private static ExecutorService executorService;

    public RemoteServiceImpl(@Value("${remote.host}") String remoteHost, @Value("${remote.port}") Integer remotePort,
                             @Value("${threadPool.corePoolSize}") Integer corePoolSize, @Value("${threadPool.maximumPoolSize}") Integer maximumPoolSize,
                             @Value("${threadPool.keepAliveTime}") Long keepAliveTime, @Value("${threadPool.capacity}") Integer capacity) {
        this.HOST = remoteHost;
        this.PORT =  remotePort;

        executorService = new ThreadPoolExecutor(
                corePoolSize, // 核心线程数
                maximumPoolSize, // 最大线程数
                keepAliveTime, // 线程活跃时间
                TimeUnit.SECONDS, // 单位分钟
                new ArrayBlockingQueue<>(capacity), // 线程容量
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
    }

    @Override
    public String RemoteCall(String content) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("content", content);
        String str = jsonObject.toJSONString();

        // 访问服务进程的套接字
        Socket socket = null;
        // List<Question> questions = new ArrayList<>();
        log.info("调用远程接口:host=>" + HOST + ",port=>" + PORT);
        try {
            // 初始化套接字，设置访问服务的主机和进程端口号，HOST是访问python进程的主机名称，可以是IP地址或者域名，PORT是python进程绑定的端口号
            socket = new Socket(HOST, PORT);
            // 获取输出流对象
            OutputStream os = socket.getOutputStream();
            PrintStream out = new PrintStream(os);
            // 发送内容
            out.print(str);
            // 告诉服务进程，内容发送完毕，可以开始处理
            out.print("over");
            // 获取服务进程的输入流
            InputStream is = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is, "utf-8"));
            String tmp = null;
            StringBuilder sb = new StringBuilder();
            // 读取内容
            while ((tmp = br.readLine()) != null)
                sb.append(tmp).append('\n');

            // 关闭socket通道和流通道
            socket.close();
            is.close();
            os.close();

            // 解析结果
            JSONObject res = JSON.parseObject(sb.toString());
            return res.toJSONString();

        } catch (IOException e) {
            // 抛出远程调用异常
            e.printStackTrace();
            throw new ScriptCallException(ResultCodeEnum.LOCAL_CALL_ERROR.getCode(),
                    ResultCodeEnum.LOCAL_CALL_ERROR.getMessage());
        }

    }

    /**
     * 待优化，线程池，线程池的优点
     * 降低系统资源消耗，通过重用已存在的线程，降低线程创建和销毁造成的消耗；
     * 提高系统响应速度，当有任务到达时，无需等待新线程的创建便能立即执行；
     * 方便线程并发数的管控，线程若是无限制的创建，不仅会额外消耗大量系统资源，更是占用过多资源而阻塞系统或oom等状况，从而降低系统的稳定性。线程池能有效管控线程，统一分配、调优，提供资源使用率；
     * 更强大的功能，线程池提供了定时、定期以及可控线程数等功能的线程池，使用方便简单。
     *
     */
    @Override
    public void executePyScript(String classesPath) {


        //1. 创建一个自定义的线程池
        /**
         * corePoolSize：核心线程数
         * maximumPoolSize：最大线程数
         * keepAliveTime：最大等待市场1s
         * capacity：等待队列
         */
        Future<Result> future = null;
        try {
            // 2、创建Callable接口实现类的对象
            RemoteThread remoteThread = new RemoteThread();
            remoteThread.setClassesPath(classesPath);

            // 3、将此Callable接口实现类的对象作为传递到FutureTask构造器中，创建FutureTask的对象 new FutureTask<>(remoteThread);
            future = executorService.submit(remoteThread);
        } catch (Exception e) {
            throw new ThreadPoolException();
        }

        /**
         * 拿到执行结果
         */
        try {
            // 4、将FutureTask的对象作为参数传递到Thread类的构造器中，创建Thread对象，并调用start()
            log.info(String.valueOf(future.get()));
        } catch (Exception e) {
            e.printStackTrace();
            throw new ScriptCallException(ResultCodeEnum.REMOTE_CALL_ERROR.getCode(),
                    ResultCodeEnum.REMOTE_CALL_ERROR.getMessage());
        } finally {
            executorService.shutdown();
        }
    }
}
